package cn.catsdad.service.impl;

import cn.catsdad.auth.util.SecurityUtil;
import cn.catsdad.base.page.PageBean;
import cn.catsdad.base.page.PageParam;
import cn.catsdad.base.service.impl.BaseCrudServiceImpl;
import cn.catsdad.dto.*;
import cn.catsdad.exception.BaseException;
import cn.catsdad.page.UserDropdownParam;
import cn.catsdad.page.UserParam;
import cn.catsdad.repository.UserRepository;
import cn.catsdad.service.api.*;
import cn.catsdad.util.StringUtil;
import cn.catsdad.vo.TreeNodeVO;
import cn.catsdad.vo.UserDropdownQueryVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户service实现
 *
 * @author xuyonglong
 * @version V1.0 2023/4/10
 */
@Service
public class UserServiceImpl extends BaseCrudServiceImpl<UserDTO, Long, UserRepository> implements UserService {

    private DepartmentUserService departmentUserService;
    private PositionUserService positionUserService;
    private RoleUserService roleUserService;
    private RoleService roleService;
    private DepartmentService departmentService;

    @Value("${token.initialPassword}")
    private String initialPassword;

    @Override
    public void pageQuery(PageBean<UserDTO> page, PageParam param) {
        page.setEntityName("UserDTO u");
        page.setQuery("select u");
        fillingParam(param);
        super.pageQuery(page, param);
    }

    @Override
    public List<UserDTO> dropDown(UserDropdownQueryVo vo) {
        PageBean<UserDTO> page = new PageBean<UserDTO>()
                .setSort(vo.getSort()).setPage(1).setSize(Integer.MAX_VALUE)
                .setEntityName("UserDTO u").setQuery("select u");
        UserDropdownParam param = new UserDropdownParam();
        param.setDepartmentIds(vo.getDepartmentIds());
        param.setRoleIds(vo.getDepartmentIds());
        param.setPositionIds(vo.getPositionIds());
        super.pageQuery(page, param);
        return page.getQueryResult();
    }

    /**
     * 根据登录名查询
     *
     * @param loginName 登录名
     * @return 用户
     */
    @Override
    public UserDTO queryUserByLoginName(String loginName) {
        UserDTO result = null;
        if (StringUtil.isNotEmpty(loginName)) {
            List<UserDTO> users = repository.findByLoginName(loginName);
            if (StringUtil.isNotEmpty(users)) {
                result = users.get(0);
                if (result.getIsDeleted()) {
                    throw new BaseException("当前用户已删除！");
                }
            } else {
                throw new BaseException("当前用户不存在！");
            }
        }
        return result;
    }

    /**
     * 登录验证
     *
     * @param user     登录的用户
     * @param password 用户密码
     */
    @Override
    public void loginPasswordValidate(UserDTO user, String password) {
        if (StringUtil.isEmpty(password)) {
            throw new BaseException("登录密码不能为空！");
        }
        if (!SecurityUtil.matchPassword(password, user.getPassword())) {
            throw new BaseException("用户名或密码错误！");
        }
    }

    @Override
    @Transactional
    public void bingRoles(Long userId, Collection<Long> roleIds) {
        roleUserService.deleteByUserIds(Collections.singleton(userId));
        List<RoleUserDTO> roleUsers = new ArrayList<>(roleIds.size());
        for (Long roleId : roleIds) {
            RoleUserDTO saveItem = new RoleUserDTO();
            saveItem.setRoleId(roleId);
            saveItem.setUserId(userId);
            roleUsers.add(saveItem);
        }
        if (StringUtil.isNotEmpty(roleUsers)) {
            roleUserService.saveAll(roleUsers);
        }
    }

    @Override
    @Transactional
    public void batchResetPassword(Collection<Long> ids) {
        if (StringUtil.isNotEmpty(ids)) {
            List<UserDTO> entities = repository.findAllById(ids);
            for (UserDTO entity : entities) {
                entity.setPassword(SecurityUtil.passwordEncode(initialPassword));
            }
            saveAll(entities);
        }
    }

    @Override
    @Transactional
    public void editPassword(UserDTO user) {
        Long id = user.getId();
        UserDTO exist = queryById(id);
        if (!SecurityUtil.matchPassword(user.getOldPassword(), exist.getPassword())) {
            throw new BaseException("密码错误！");
        }
        exist.setPassword(SecurityUtil.passwordEncode(user.getPassword()));
        update(exist);
    }

    @Override
    @Transactional
    public UserDTO save(UserDTO entity) {
        //保存验证
        validation(entity);
        //保存关联数据
        transientDataSave(entity);
        entity.setPassword(SecurityUtil.passwordEncode(initialPassword));
        return super.save(entity);
    }

    @Override
    @Transactional
    public UserDTO update(UserDTO entity) {
        //保存验证
        validation(entity);
        //保存关联数据
        transientDataSave(entity);
        return super.update(entity);
    }

    @Override
    public UserDTO queryById(Long aLong) {
        UserDTO result = super.queryById(aLong);
        fillingTransientFields(Collections.singletonList(result));
        return result;
    }

    @Override
    @Transactional
    public void logicDeleteByIds(Collection<Long> longs) {
        if (StringUtil.isNotEmpty(longs)) {
            departmentUserService.deleteByUserIdIn(longs);
            positionUserService.deleteByUserIdIn(longs);
            roleUserService.deleteByUserIds(longs);
        }
        super.logicDeleteByIds(longs);
    }

    @Override
    public List<TreeNodeVO> queryMenuTree(Long userId) {
        List<Long> roleIds = roleUserService.queryRoleIdByUserIdIn(Collections.singleton(userId));
        return roleService.queryMenuTree(roleIds);
    }

    /**
     * 根据id统计
     *
     * @param id 用户id
     * @return 数量
     */
    @Override
    public int countById(Long id) {
        int result = 0;
        if (id != null) {
            result = repository.countByIdAndIsDeletedFalse(id);
        }
        return result;
    }

    /**
     * 保存验证
     *
     * @param entity 保存的实体
     */
    private void validation(UserDTO entity) {
        Integer count = repository.countByLoginNameAndIdNotAndIsDeletedFalse(entity.getLoginName(), entity.getId());
        if (count > 0) {
            throw new BaseException("已存在当前登录名的用户！");
        }
    }

    /**
     * 保存临时数据
     *
     * @param user 保存的实体
     */
    private void transientDataSave(UserDTO user) {
        departmentUserService.deleteByUserIdIn(Collections.singleton(user.getId()));
        positionUserService.deleteByUserIdIn(Collections.singleton(user.getId()));
        roleUserService.deleteByUserIds(Collections.singleton(user.getId()));
        Collection<Long> positionIds = user.getPositionIds();
        Long departmentId = user.getDepartmentId();
        List<PositionUserDTO> positionUsers = new ArrayList<>(positionIds.size());
        for (Long positionId : positionIds) {
            PositionUserDTO positionUser = new PositionUserDTO();
            positionUser.setUserId(user.getId());
            positionUser.setPositionId(positionId);
            positionUsers.add(positionUser);
        }
        DepartmentUserDTO departmentUser = new DepartmentUserDTO();
        departmentUser.setDepartmentId(departmentId);
        departmentUser.setUserId(user.getId());
        departmentUserService.deleteByUserIdIn(Collections.singleton(user.getId()));
        departmentUserService.save(departmentUser);
        if (StringUtil.isNotEmpty(positionUsers)) {
            positionUserService.saveAll(positionUsers);
        }
    }

    /**
     * 填充前端显示的临时字段
     *
     * @param users 需要填充的用户列表
     */
    private void fillingTransientFields(List<UserDTO> users) {
        List<Long> ids = users.stream().map(UserDTO::getId).collect(Collectors.toList());
        List<DepartmentUserDTO> departmentUsers = new ArrayList<>();
        List<PositionUserDTO> positionUsers = new ArrayList<>();
        Map<Long, List<Long>> userIdRoleIds = new HashMap<>();
        List<RoleDTO> roles = new ArrayList<>();
        if (StringUtil.isNotEmpty(ids)) {
            userIdRoleIds = roleUserService.queryRolesByUserIds(ids);
            if (StringUtil.isNotEmpty(userIdRoleIds)) {
                Set<Long> roleIds = new HashSet<>();
                for (Map.Entry<Long, List<Long>> entry : userIdRoleIds.entrySet()) {
                    roleIds.addAll(entry.getValue());
                }
                if (StringUtil.isNotEmpty(roleIds)) {
                    roles = roleService.queryByIds(roleIds);
                }
            }
            departmentUsers = departmentUserService.queryByUserIdIn(ids);
            positionUsers = positionUserService.queryByUserIds(ids);
        }
        for (UserDTO user : users) {
            List<Long> roleIds = userIdRoleIds.get(user.getId()) == null ? new ArrayList<>() : userIdRoleIds.get(user.getId());
            user.setRoles(roles.stream().filter(r -> roleIds.contains(r.getId())).collect(Collectors.toList()));
            Optional<DepartmentUserDTO> departmentUser = departmentUsers.stream().filter(d -> d.getUserId().equals(user.getId())).findFirst();
            departmentUser.ifPresent(d -> user.setDepartmentId(d.getDepartmentId()));
            user.setPositionIds(positionUsers.stream()
                    .filter(p -> p.getUserId().equals(user.getId())).map(PositionUserDTO::getPositionId)
                    .distinct().collect(Collectors.toList()));
        }
    }

    /**
     * 填充分页查询参数
     *
     * @param param 查询参数对象
     */
    private void fillingParam(PageParam param) {
        UserParam userParam = (UserParam) param;
        Long departmentId = userParam.getDepartmentId();
        if (departmentId != null) {
            Set<Long> childrenIds = departmentService.queryChildrenIds(departmentId);
            userParam.setAllDepartmentIds(new ArrayList<>(childrenIds));
        }
    }

    @Autowired
    public void setDepartmentUserService(DepartmentUserService departmentUserService) {
        this.departmentUserService = departmentUserService;
    }

    @Autowired
    public void setPositionUserService(PositionUserService positionUserService) {
        this.positionUserService = positionUserService;
    }

    @Autowired
    public void setRoleUserService(RoleUserService roleUserService) {
        this.roleUserService = roleUserService;
    }

    @Autowired
    public void setRoleService(RoleService roleService) {
        this.roleService = roleService;
    }

    @Autowired
    public void setDepartmentService(DepartmentService departmentService) {
        this.departmentService = departmentService;
    }
}
